package ths

import (
	"fmt"
	"gitee.com/gcom/gbox/errors"
	s "gitee.com/gcom/stockdock/core/support"
	"gitee.com/gcom/stockdock/crawler"
	"strings"
	"time"
)

// StockHistThs 同花顺K线
type StockHistThs struct {
	Code     string    // 股票代码
	Name     string    // 股票名称
	Date     time.Time // 报价日
	Open     float64
	High     float64
	Low      float64
	Close    float64
	PreClose float64 // 昨日收盘价
	Volume   int64
}

// IndustryQuoteThs 同花顺行业信息
type IndustryQuoteThs struct {
	Name        string  // 行业名称
	Code        string  // 行业编码
	Percent     float64 // 行业涨跌幅，单位：%
	Volume      int64   // 成交量，单位：万手
	Amount      float64 // 成交额，单位：亿元
	FlowIn      float64 // 流入金额，单位：亿元
	Up          int     // 上涨家数
	Down        int     // 下跌家数
	Avg         float64 // 均价
	LeadCode    string  // 领涨股编码
	LeadName    string  // 领涨股名称
	LeadPrice   float64 // 领涨股最新价
	LeadPercent float64 // 领涨股涨跌幅(%)
}

// DailyQuoteThs 同花顺日行情
type DailyQuoteThs struct {
	Name               string  // 股票名称
	Code               string  // 股票编码
	Price              float64 // 现价
	Amplitude          float64 // 振幅（%）
	Change             float64 // 涨跌额，单位：元
	Percent            float64 // 涨幅（％），为负表示跌幅
	Amount             float64 // 成交额，单位：元
	TurnoverRate       float64 // 换手率，%
	VolumeRatio        float64 // 量比
	FloatMarketCapital float64 // 流通市值，元
	FloatShare         int64   // 流通股本，股
	PE                 float64 // 市盈率
}

// StockQuoteThs 同花顺股票指标
type StockQuoteThs struct {
	Code         string    // 股票编码
	Date         time.Time // 报价日
	Name         string    // 股票名称
	Close        float64   // 收盘价
	High         float64   // 最高价
	Low          float64   // 最低价
	Open         float64   // 开盘价
	PreClose     float64   // 昨收价
	LimitUp      float64   // 涨停价，元
	LimitDown    float64   // 跌停价，元
	PB           float64   `gorm:"column:pb"`         // 市静率
	PE           float64   `gorm:"column:pe"`         // 静态市盈率
	PEForward    float64   `gorm:"column:pe_forward"` // 动态市盈率
	Change       float64   // 涨跌额（元）
	PctChg       float64   // 涨跌幅（%）
	Amplitude    float64   // 振幅（%）
	TurnoverRate float64   // 换手率（%）
	VolumeRatio  float64   // 量比
	Volume       int64     // 成交量（股）
	Amount       float64   // 成交额（元）
	TotalShare   int64     // 总股本
	FloatShare   int64     // 流通股本
	TotalMV      float64   `gorm:"column:total_mv"` // 总市值（元）
	FloatMV      float64   `gorm:"column:float_mv"` // 流通市值（元）
	Timestamp    time.Time // 交易日期
}

// CompInfoThs 同花顺上市公司信息
type CompInfoThs struct {
	Code              string    // 股票编码
	Name              string    // 股票名称
	CompName          string    // 公司名称
	CompEnName        string    // 公司英文名称
	Area              string    // 所属地域
	Industry          string    // 所属行业
	UsedNames         string    // 曾用名
	Website           string    // 公司网址
	MainBiz           string    // 主营业务
	Product           string    // 产品名称
	CtlShareholder    string    // 控股股东
	ActualCtrl        string    // 实际控制人
	UltimateCtrl      string    // 最终控制人
	Chairman          string    // 董事长
	Secretary         string    // 董秘
	Representative    string    // 法人代表
	GeneralManager    string    // 总经理
	RegisterFund      float64   // 注册资金
	EmployeeNum       int       // 员工人数
	Address           string    // 办公地址
	Intro             string    // 公司简介
	EastDate          time.Time // 成立日期
	ListDate          time.Time // 上市日期
	IssuePrice        float64   // 发行价格
	InitOpenPrice     float64   // 首日开盘价
	IssuePE           float64   // 发行市盈率
	Demand2OfferRatio float64   // 中签率，％
	IssueVolume       int64     // 发行数量
	InvestAmount      float64   // 募集资金
	Underwriter       string    // 主承销商
	Sponsor           string    // 保荐人
	History           string    // 历史沿革
}

type StockCodeAndName struct {
	Code string
	Name string
}

// QuotePageEventThs 同花顺股票行情分页消息
type QuotePageEventThs struct {
	Data      []*DailyQuoteThs
	PageNo    int
	PageCount int
	Err       error
}

// StocksQuery 股票精确匹配查询
type StocksQuery struct {
	Codes    []string `validate:"omitempty,dive,number,len=6"`
	Industry string   `validate:"omitempty,number,len=6"`
	Pn       int
	Ps       int //`validate:"lte=500"`
}

// StockHistEvent 同花顺股票历史行情消息
type StockHistEvent struct {
	Code string
	Name string
	Data []*StockHistThs
	Err  error
}

type StockQuoteCrawlRequest struct {
	Ctx           s.TracerCtx
	Code          string
	Tm            time.Duration
	TryCount      int
	OnValueFunc   crawler.CrawlOnValue
	OnErrorFunc   crawler.CrawlOnError
	OnDiscardFunc crawler.CrawlOnDiscardFunc
}

func (r *StockQuoteCrawlRequest) RequestJs() string {
	return fmt.Sprintf(`function quotebridge_v2_realhead_hs_%s_last(json) {window.$$val=JSON.stringify(json);alert('%s');};
				$.get("http://d.10jqka.com.cn/v2/realhead/hs_%s/last.js").fail(v => {
					if (v.status >= 300 ) alert("ERR-" + v.status + ":" + "%s");
				}); 1;`, r.Code, r.Code, r.Code, r.Code)
}

func (r *StockQuoteCrawlRequest) Timeout() time.Duration {
	if r.Tm == 0 {
		return 10 * time.Second
	}
	return r.Tm
}

func (r *StockQuoteCrawlRequest) EvalJs(alert string) (js string, strategy crawler.CrawlEvalStrategy) {
	if strings.HasPrefix(alert, "ERR-") {
		arr := strings.Split(alert[4:], ":")
		if arr[1] == r.Code {
			if arr[0] == "403" {
				r.OnError(errors.Wrap(crawler.CrawlErrForbidden, "获取股票(%s)历史报价失败，已被禁止访问"))
			}
			r.OnDiscardFunc(r, errors.New(fmt.Sprintf("获取股票(%s)历史报价失败，放弃本支股票，status=%s", r.Code, arr[0])))
			return "", crawler.CrawlEvalSkipRequest
		}
	} else if alert == r.Code {
		return "window.$$val", crawler.CrawlEvalOK
	}
	r.Ctx.Logger.Infof("discarded event: %s", alert)
	return "", crawler.CrawlEvalSkipEvent
}

func (r *StockQuoteCrawlRequest) OnValue(val string) {
	r.OnValueFunc(r, val)
}

func (r *StockQuoteCrawlRequest) OnError(err error) {
	r.OnErrorFunc(r, err)
}

func (r *StockQuoteCrawlRequest) CanRetry(err error) (d time.Duration, ok bool) {
	if r.TryCount >= 2 {
		return 0, false
	}
	if errors.HasError(err, crawler.CrawlErrTimeout) {
		r.TryCount += 1
		d := time.Duration(4*r.TryCount-3) * time.Minute
		s.WriteErrEvent(r.Ctx, fmt.Sprintf("获取股票(%s)报价失败，等待%v第%d次重试", r.Code, d, r.TryCount), err)
		return d, true
	}
	return 0, false
}

type StockHistCrawlRequest struct {
	Ctx           s.TracerCtx
	Code          string
	Tm            time.Duration
	TryCount      int
	OnValueFunc   crawler.CrawlOnValue
	OnErrorFunc   crawler.CrawlOnError
	OnDiscardFunc crawler.CrawlOnDiscardFunc
}

func (r *StockHistCrawlRequest) RequestJs() string {
	return fmt.Sprintf(`function quotebridge_v6_line_hs_%s_00_all(json) {window.$$val=JSON.stringify(json);alert('%s');};
				$.get("http://d.10jqka.com.cn/v6/line/hs_%s/00/all.js").fail(v => {
					if (v.status >= 300 ) alert("ERR-" + v.status + ":" + "%s");
				}); 1;`, r.Code, r.Code, r.Code, r.Code)
}

func (r *StockHistCrawlRequest) Timeout() time.Duration {
	if r.Tm == 0 {
		return 10 * time.Second
	}
	return r.Tm
}

func (r *StockHistCrawlRequest) EvalJs(alert string) (js string, strategy crawler.CrawlEvalStrategy) {
	if strings.HasPrefix(alert, "ERR-") {
		arr := strings.Split(alert[4:], ":")
		if arr[1] == r.Code {
			if arr[0] == "403" {
				r.OnError(errors.Wrap(crawler.CrawlErrForbidden, "获取股票(%s)历史报价失败，已被禁止访问"))
			}
			r.OnDiscardFunc(r, errors.New(fmt.Sprintf("获取股票(%s)历史报价失败，放弃本支股票，status=%s", r.Code, arr[0])))
			return "", crawler.CrawlEvalSkipRequest
		}
	} else if alert == r.Code {
		return "window.$$val", crawler.CrawlEvalOK
	}
	return "", crawler.CrawlEvalSkipEvent
}

func (r *StockHistCrawlRequest) OnValue(val string) {
	r.OnValueFunc(r, val)
}

func (r *StockHistCrawlRequest) OnError(err error) {
	r.OnErrorFunc(r, err)
}

func (r *StockHistCrawlRequest) CanRetry(err error) (d time.Duration, ok bool) {
	if r.TryCount >= 2 {
		return 0, false
	}
	if errors.HasError(err, crawler.CrawlErrTimeout) {
		r.TryCount += 1
		d := time.Duration(4*r.TryCount-3) * time.Minute
		s.WriteErrEvent(r.Ctx, fmt.Sprintf("获取股票(%s)历史报价失败，等待%v第%d次重试", r.Code, d, r.TryCount), err)
		return d, true
	}
	return 0, false
}
